// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stddef.h>

#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "cc/layers/layer_iterator.h"
#include "cc/output/copy_output_request.h"
#include "cc/output/copy_output_result.h"
#include "cc/output/direct_renderer.h"
#include "cc/surfaces/display.h"
#include "cc/test/fake_content_layer_client.h"
#include "cc/test/fake_output_surface.h"
#include "cc/test/fake_picture_layer.h"
#include "cc/test/layer_tree_test.h"
#include "cc/test/test_compositor_frame_sink.h"
#include "cc/trees/layer_tree_impl.h"
#include "gpu/GLES2/gl2extchromium.h"

namespace cc {
namespace {

    // These tests only use direct rendering, as there is no output to copy for
    // delegated renderers.
    class LayerTreeHostCopyRequestTest : public LayerTreeTest {
    };

    class LayerTreeHostCopyRequestTestMultipleRequests
        : public LayerTreeHostCopyRequestTest {
    protected:
        void SetupTree() override
        {
            root = FakePictureLayer::Create(&client_);
            root->SetBounds(gfx::Size(20, 20));

            child = FakePictureLayer::Create(&client_);
            child->SetBounds(gfx::Size(10, 10));
            root->AddChild(child);

            grand_child = FakePictureLayer::Create(&client_);
            grand_child->SetBounds(gfx::Size(5, 5));
            child->AddChild(grand_child);

            layer_tree()->SetRootLayer(root);
            LayerTreeHostCopyRequestTest::SetupTree();
            client_.set_bounds(root->bounds());
        }

        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void DidCommit() override { WaitForCallback(); }

        void WaitForCallback()
        {
            base::ThreadTaskRunnerHandle::Get()->PostTask(
                FROM_HERE,
                base::Bind(&LayerTreeHostCopyRequestTestMultipleRequests::NextStep,
                    base::Unretained(this)));
        }

        void NextStep()
        {
            int frame = layer_tree_host()->SourceFrameNumber();
            switch (frame) {
            case 1:
                child->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest(
                    base::Bind(&LayerTreeHostCopyRequestTestMultipleRequests::
                                   CopyOutputCallback,
                        base::Unretained(this), 0)));
                EXPECT_EQ(0u, callbacks_.size());
                break;
            case 2:
                // This commit is triggered by the copy request having been completed.
                break;
            case 3:
                if (callbacks_.size() < 1u) {
                    WaitForCallback();
                    return;
                }
                EXPECT_EQ(1u, callbacks_.size());
                EXPECT_EQ(gfx::Size(10, 10).ToString(), callbacks_[0].ToString());

                child->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest(
                    base::Bind(&LayerTreeHostCopyRequestTestMultipleRequests::
                                   CopyOutputCallback,
                        base::Unretained(this), 1)));
                root->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest(
                    base::Bind(&LayerTreeHostCopyRequestTestMultipleRequests::
                                   CopyOutputCallback,
                        base::Unretained(this), 2)));
                grand_child->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest(
                    base::Bind(&LayerTreeHostCopyRequestTestMultipleRequests::
                                   CopyOutputCallback,
                        base::Unretained(this), 3)));
                EXPECT_EQ(1u, callbacks_.size());
                break;
            case 4:
                // This commit is triggered by the copy request having been completed.
                break;
            case 5:
                if (callbacks_.size() < 4u) {
                    WaitForCallback();
                    return;
                }
                EXPECT_EQ(4u, callbacks_.size());

                // The |child| was copied to a bitmap and passed back in Case 1.
                EXPECT_EQ(gfx::Size(10, 10).ToString(), callbacks_[0].ToString());

                // The |child| was copied to a bitmap and passed back in Case 2.
                EXPECT_EQ(gfx::Size(10, 10).ToString(), callbacks_[1].ToString());
                // The |root| was copied to a bitmap and passed back also in Case 2.
                EXPECT_EQ(gfx::Size(20, 20).ToString(), callbacks_[2].ToString());
                // The |grand_child| was copied to a bitmap and passed back in Case 2.
                EXPECT_EQ(gfx::Size(5, 5).ToString(), callbacks_[3].ToString());
                EndTest();
                break;
            }
        }

        void CopyOutputCallback(size_t id, std::unique_ptr<CopyOutputResult> result)
        {
            EXPECT_TRUE(layer_tree_host()->GetTaskRunnerProvider()->IsMainThread());
            EXPECT_TRUE(result->HasBitmap());
            std::unique_ptr<SkBitmap> bitmap = result->TakeBitmap();
            EXPECT_EQ(result->size().ToString(),
                gfx::Size(bitmap->width(), bitmap->height()).ToString());
            callbacks_[id] = result->size();
        }

        void AfterTest() override { EXPECT_EQ(4u, callbacks_.size()); }

        std::unique_ptr<OutputSurface> CreateDisplayOutputSurfaceOnThread(
            scoped_refptr<ContextProvider> compositor_context_provider) override
        {
            if (!use_gl_renderer_) {
                return FakeOutputSurface::CreateSoftware(
                    base::WrapUnique(new SoftwareOutputDevice));
            }

            scoped_refptr<TestContextProvider> display_context_provider = TestContextProvider::Create();
            TestContextSupport* context_support = display_context_provider->support();
            context_support->set_out_of_order_callbacks(out_of_order_callbacks_);
            display_context_provider->BindToCurrentThread();

            return FakeOutputSurface::Create3d(std::move(display_context_provider));
        }

        bool use_gl_renderer_;
        bool out_of_order_callbacks_ = false;
        std::map<size_t, gfx::Size> callbacks_;
        FakeContentLayerClient client_;
        scoped_refptr<FakePictureLayer> root;
        scoped_refptr<FakePictureLayer> child;
        scoped_refptr<FakePictureLayer> grand_child;
    };

    TEST_F(LayerTreeHostCopyRequestTestMultipleRequests,
        GLRenderer_RunSingleThread)
    {
        use_gl_renderer_ = true;
        RunTest(CompositorMode::SINGLE_THREADED);
    }

    TEST_F(LayerTreeHostCopyRequestTestMultipleRequests,
        GLRenderer_RunMultiThread)
    {
        use_gl_renderer_ = true;
        RunTest(CompositorMode::THREADED);
    }

    TEST_F(LayerTreeHostCopyRequestTestMultipleRequests,
        GLRenderer_RunSingleThread_OutOfOrderCallbacks)
    {
        use_gl_renderer_ = true;
        out_of_order_callbacks_ = true;
        RunTest(CompositorMode::SINGLE_THREADED);
    }

    TEST_F(LayerTreeHostCopyRequestTestMultipleRequests,
        GLRenderer_RunMultiThread_OutOfOrderCallbacks)
    {
        use_gl_renderer_ = true;
        out_of_order_callbacks_ = true;
        RunTest(CompositorMode::THREADED);
    }

    TEST_F(LayerTreeHostCopyRequestTestMultipleRequests,
        SoftwareRenderer_RunSingleThread)
    {
        use_gl_renderer_ = false;
        RunTest(CompositorMode::SINGLE_THREADED);
    }

    TEST_F(LayerTreeHostCopyRequestTestMultipleRequests,
        SoftwareRenderer_RunMultiThread)
    {
        use_gl_renderer_ = false;
        RunTest(CompositorMode::THREADED);
    }

    // TODO(crbug.com/564832): Remove this test when the workaround it tests is no
    // longer needed.
    class LayerTreeHostCopyRequestCompletionCausesCommit
        : public LayerTreeHostCopyRequestTest {
    protected:
        void SetupTree() override
        {
            root_ = FakePictureLayer::Create(&client_);
            root_->SetBounds(gfx::Size(20, 20));

            layer_ = FakePictureLayer::Create(&client_);
            layer_->SetBounds(gfx::Size(15, 15));
            root_->AddChild(layer_);

            layer_tree()->SetRootLayer(root_);
            LayerTreeHostCopyRequestTest::SetupTree();
            client_.set_bounds(root_->bounds());
        }

        void BeginTest() override
        {
            PostSetNeedsCommitToMainThread();
        }

        void DidCommit() override
        {
            int frame = layer_tree_host()->SourceFrameNumber();
            switch (frame) {
            case 1:
                layer_->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest(
                    base::Bind(&LayerTreeHostCopyRequestCompletionCausesCommit::
                                   CopyOutputCallback)));
                break;
            case 2:
                // This commit is triggered by the copy request.
                break;
            case 3:
                // This commit is triggered by the completion of the copy request.
                EndTest();
                break;
            }
        }

        static void CopyOutputCallback(std::unique_ptr<CopyOutputResult> result)
        {
            EXPECT_FALSE(result->IsEmpty());
        }

        void AfterTest() override { }

        FakeContentLayerClient client_;
        scoped_refptr<FakePictureLayer> root_;
        scoped_refptr<FakePictureLayer> layer_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCopyRequestCompletionCausesCommit);

    class LayerTreeHostCopyRequestTestLayerDestroyed
        : public LayerTreeHostCopyRequestTest {
    protected:
        void SetupTree() override
        {
            root_ = FakePictureLayer::Create(&client_);
            root_->SetBounds(gfx::Size(20, 20));

            main_destroyed_ = FakePictureLayer::Create(&client_);
            main_destroyed_->SetBounds(gfx::Size(15, 15));
            root_->AddChild(main_destroyed_);

            impl_destroyed_ = FakePictureLayer::Create(&client_);
            impl_destroyed_->SetBounds(gfx::Size(10, 10));
            root_->AddChild(impl_destroyed_);

            layer_tree()->SetRootLayer(root_);
            LayerTreeHostCopyRequestTest::SetupTree();
            client_.set_bounds(root_->bounds());
        }

        void BeginTest() override
        {
            callback_count_ = 0;
            PostSetNeedsCommitToMainThread();
        }

        void DidCommit() override
        {
            int frame = layer_tree_host()->SourceFrameNumber();
            switch (frame) {
            case 1:
                main_destroyed_->RequestCopyOfOutput(
                    CopyOutputRequest::CreateBitmapRequest(base::Bind(
                        &LayerTreeHostCopyRequestTestLayerDestroyed::CopyOutputCallback,
                        base::Unretained(this))));
                impl_destroyed_->RequestCopyOfOutput(
                    CopyOutputRequest::CreateBitmapRequest(base::Bind(
                        &LayerTreeHostCopyRequestTestLayerDestroyed::CopyOutputCallback,
                        base::Unretained(this))));
                EXPECT_EQ(0, callback_count_);

                // Destroy the main thread layer right away.
                main_destroyed_->RemoveFromParent();
                main_destroyed_ = NULL;

                // Should callback with a NULL bitmap.
                EXPECT_EQ(1, callback_count_);

                // Prevent drawing so we can't make a copy of the impl_destroyed layer.
                layer_tree()->SetViewportSize(gfx::Size());
                break;
            case 2:
                // Flush the message loops and make sure the callbacks run.
                layer_tree_host()->SetNeedsCommit();
                break;
            case 3:
                // No drawing means no readback yet.
                EXPECT_EQ(1, callback_count_);

                // Destroy the impl thread layer.
                impl_destroyed_->RemoveFromParent();
                impl_destroyed_ = NULL;

                // No callback yet because it's on the impl side.
                EXPECT_EQ(1, callback_count_);
                break;
            case 4:
                // Flush the message loops and make sure the callbacks run.
                layer_tree_host()->SetNeedsCommit();
                break;
            case 5:
                // We should get another callback with a NULL bitmap.
                EXPECT_EQ(2, callback_count_);
                EndTest();
                break;
            }
        }

        void CopyOutputCallback(std::unique_ptr<CopyOutputResult> result)
        {
            EXPECT_TRUE(layer_tree_host()->GetTaskRunnerProvider()->IsMainThread());
            EXPECT_TRUE(result->IsEmpty());
            ++callback_count_;
        }

        void AfterTest() override { }

        int callback_count_;
        FakeContentLayerClient client_;
        scoped_refptr<FakePictureLayer> root_;
        scoped_refptr<FakePictureLayer> main_destroyed_;
        scoped_refptr<FakePictureLayer> impl_destroyed_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCopyRequestTestLayerDestroyed);

    class LayerTreeHostCopyRequestTestInHiddenSubtree
        : public LayerTreeHostCopyRequestTest {
    protected:
        void SetupTree() override
        {
            root_ = FakePictureLayer::Create(&client_);
            root_->SetBounds(gfx::Size(20, 20));

            grand_parent_layer_ = FakePictureLayer::Create(&client_);
            grand_parent_layer_->SetBounds(gfx::Size(15, 15));
            root_->AddChild(grand_parent_layer_);

            // parent_layer_ owns a render surface.
            parent_layer_ = FakePictureLayer::Create(&client_);
            parent_layer_->SetBounds(gfx::Size(15, 15));
            parent_layer_->SetForceRenderSurfaceForTesting(true);
            grand_parent_layer_->AddChild(parent_layer_);

            copy_layer_ = FakePictureLayer::Create(&client_);
            copy_layer_->SetBounds(gfx::Size(10, 10));
            parent_layer_->AddChild(copy_layer_);

            layer_tree()->SetRootLayer(root_);
            LayerTreeHostCopyRequestTest::SetupTree();
            client_.set_bounds(root_->bounds());
        }

        void AddCopyRequest(Layer* layer)
        {
            layer->RequestCopyOfOutput(
                CopyOutputRequest::CreateBitmapRequest(base::Bind(
                    &LayerTreeHostCopyRequestTestInHiddenSubtree::CopyOutputCallback,
                    base::Unretained(this))));
        }

        void BeginTest() override
        {
            callback_count_ = 0;
            PostSetNeedsCommitToMainThread();

            AddCopyRequest(copy_layer_.get());
        }

        void CopyOutputCallback(std::unique_ptr<CopyOutputResult> result)
        {
            ++callback_count_;
            EXPECT_TRUE(layer_tree_host()->GetTaskRunnerProvider()->IsMainThread());
            EXPECT_EQ(copy_layer_->bounds().ToString(), result->size().ToString())
                << callback_count_;
            switch (callback_count_) {
            case 1:
                // Hide the copy request layer.
                grand_parent_layer_->SetHideLayerAndSubtree(false);
                parent_layer_->SetHideLayerAndSubtree(false);
                copy_layer_->SetHideLayerAndSubtree(true);
                AddCopyRequest(copy_layer_.get());
                break;
            case 2:
                // Hide the copy request layer's parent only.
                grand_parent_layer_->SetHideLayerAndSubtree(false);
                parent_layer_->SetHideLayerAndSubtree(true);
                copy_layer_->SetHideLayerAndSubtree(false);
                AddCopyRequest(copy_layer_.get());
                break;
            case 3:
                // Hide the copy request layer's grand parent only.
                grand_parent_layer_->SetHideLayerAndSubtree(true);
                parent_layer_->SetHideLayerAndSubtree(false);
                copy_layer_->SetHideLayerAndSubtree(false);
                AddCopyRequest(copy_layer_.get());
                break;
            case 4:
                // Hide the copy request layer's parent and grandparent.
                grand_parent_layer_->SetHideLayerAndSubtree(true);
                parent_layer_->SetHideLayerAndSubtree(true);
                copy_layer_->SetHideLayerAndSubtree(false);
                AddCopyRequest(copy_layer_.get());
                break;
            case 5:
                // Hide the copy request layer as well as its parent and grandparent.
                grand_parent_layer_->SetHideLayerAndSubtree(true);
                parent_layer_->SetHideLayerAndSubtree(true);
                copy_layer_->SetHideLayerAndSubtree(true);
                AddCopyRequest(copy_layer_.get());
                break;
            case 6:
                EndTest();
                break;
            }
        }

        void AfterTest() override { }

        int callback_count_;
        FakeContentLayerClient client_;
        scoped_refptr<FakePictureLayer> root_;
        scoped_refptr<FakePictureLayer> grand_parent_layer_;
        scoped_refptr<FakePictureLayer> parent_layer_;
        scoped_refptr<FakePictureLayer> copy_layer_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCopyRequestTestInHiddenSubtree);

    class LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest
        : public LayerTreeHostCopyRequestTest {
    protected:
        void SetupTree() override
        {
            root_ = FakePictureLayer::Create(&client_);
            root_->SetBounds(gfx::Size(20, 20));

            grand_parent_layer_ = FakePictureLayer::Create(&client_);
            grand_parent_layer_->SetBounds(gfx::Size(15, 15));
            grand_parent_layer_->SetHideLayerAndSubtree(true);
            root_->AddChild(grand_parent_layer_);

            // parent_layer_ owns a render surface.
            parent_layer_ = FakePictureLayer::Create(&client_);
            parent_layer_->SetBounds(gfx::Size(15, 15));
            parent_layer_->SetForceRenderSurfaceForTesting(true);
            grand_parent_layer_->AddChild(parent_layer_);

            copy_layer_ = FakePictureLayer::Create(&client_);
            copy_layer_->SetBounds(gfx::Size(10, 10));
            parent_layer_->AddChild(copy_layer_);

            layer_tree()->SetRootLayer(root_);
            LayerTreeHostCopyRequestTest::SetupTree();
            client_.set_bounds(root_->bounds());
        }

        std::unique_ptr<TestCompositorFrameSink> CreateCompositorFrameSink(
            scoped_refptr<ContextProvider> compositor_context_provider,
            scoped_refptr<ContextProvider> worker_context_provider) override
        {
            auto frame_sink = LayerTreeHostCopyRequestTest::CreateCompositorFrameSink(
                std::move(compositor_context_provider),
                std::move(worker_context_provider));
            frame_sink_ = frame_sink.get();
            return frame_sink;
        }

        void BeginTest() override
        {
            PostSetNeedsCommitToMainThread();

            copy_layer_->RequestCopyOfOutput(
                CopyOutputRequest::CreateBitmapRequest(base::Bind(
                    &LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest::
                        CopyOutputCallback,
                    base::Unretained(this))));
        }

        void CopyOutputCallback(std::unique_ptr<CopyOutputResult> result)
        {
            EXPECT_TRUE(layer_tree_host()->GetTaskRunnerProvider()->IsMainThread());
            EXPECT_EQ(copy_layer_->bounds().ToString(), result->size().ToString());
            EndTest();
        }

        void DisplayWillDrawAndSwapOnThread(
            bool will_draw_and_swap,
            const RenderPassList& render_passes) override
        {
            EXPECT_TRUE(will_draw_and_swap) << did_swap_;
            if (did_swap_) {
                // TODO(crbug.com/564832): Ignore the extra frame that occurs due to copy
                // completion. This can be removed when the extra commit is removed.
                EXPECT_EQ(1u, render_passes.size());
                return;
            }

            EXPECT_EQ(2u, render_passes.size());
            // The root pass is the back of the list.
            copy_layer_render_pass_id = render_passes[0]->id;
            parent_render_pass_id = render_passes[1]->id;
        }

        void DisplayDidDrawAndSwapOnThread() override
        {
            DirectRenderer* renderer = frame_sink_->display()->renderer_for_testing();

            // |parent| owns a surface, but it was hidden and not part of the copy
            // request so it should not allocate any resource.
            EXPECT_FALSE(
                renderer->HasAllocatedResourcesForTesting(parent_render_pass_id));

            // TODO(crbug.com/564832): Ignore the extra frame that occurs due to copy
            // completion. This can be removed when the extra commit is removed.
            if (did_swap_) {
                EXPECT_FALSE(
                    renderer->HasAllocatedResourcesForTesting(copy_layer_render_pass_id));
            } else {
                // |copy_layer| should have been rendered to a texture since it was needed
                // for a copy request.
                EXPECT_TRUE(
                    renderer->HasAllocatedResourcesForTesting(copy_layer_render_pass_id));
            }

            did_swap_ = true;
        }

        void AfterTest() override { EXPECT_TRUE(did_swap_); }

        int parent_render_pass_id = 0;
        int copy_layer_render_pass_id = 0;
        TestCompositorFrameSink* frame_sink_ = nullptr;
        bool did_swap_ = false;
        FakeContentLayerClient client_;
        scoped_refptr<FakePictureLayer> root_;
        scoped_refptr<FakePictureLayer> grand_parent_layer_;
        scoped_refptr<FakePictureLayer> parent_layer_;
        scoped_refptr<FakePictureLayer> copy_layer_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(
        LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest);

    class LayerTreeHostCopyRequestTestClippedOut
        : public LayerTreeHostCopyRequestTest {
    protected:
        void SetupTree() override
        {
            root_ = FakePictureLayer::Create(&client_);
            root_->SetBounds(gfx::Size(20, 20));

            parent_layer_ = FakePictureLayer::Create(&client_);
            parent_layer_->SetBounds(gfx::Size(15, 15));
            parent_layer_->SetMasksToBounds(true);
            root_->AddChild(parent_layer_);

            copy_layer_ = FakePictureLayer::Create(&client_);
            copy_layer_->SetPosition(gfx::PointF(15.f, 15.f));
            copy_layer_->SetBounds(gfx::Size(10, 10));
            parent_layer_->AddChild(copy_layer_);

            layer_tree()->SetRootLayer(root_);
            LayerTreeHostCopyRequestTest::SetupTree();
            client_.set_bounds(root_->bounds());
        }

        void BeginTest() override
        {
            PostSetNeedsCommitToMainThread();

            copy_layer_->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest(
                base::Bind(&LayerTreeHostCopyRequestTestClippedOut::CopyOutputCallback,
                    base::Unretained(this))));
        }

        void CopyOutputCallback(std::unique_ptr<CopyOutputResult> result)
        {
            // We should still get the content even if the copy requested layer was
            // completely clipped away.
            EXPECT_TRUE(layer_tree_host()->GetTaskRunnerProvider()->IsMainThread());
            EXPECT_EQ(gfx::Size(10, 10).ToString(), result->size().ToString());
            EndTest();
        }

        void AfterTest() override { }

        FakeContentLayerClient client_;
        scoped_refptr<FakePictureLayer> root_;
        scoped_refptr<FakePictureLayer> parent_layer_;
        scoped_refptr<FakePictureLayer> copy_layer_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCopyRequestTestClippedOut);

    class LayerTreeHostCopyRequestTestScaledLayer
        : public LayerTreeHostCopyRequestTest {
    protected:
        void SetupTree() override
        {
            root_ = Layer::Create();
            root_->SetBounds(gfx::Size(20, 20));

            gfx::Transform scale;
            scale.Scale(2, 2);

            copy_layer_ = Layer::Create();
            copy_layer_->SetBounds(gfx::Size(10, 10));
            copy_layer_->SetTransform(scale);
            root_->AddChild(copy_layer_);

            child_layer_ = FakePictureLayer::Create(&client_);
            child_layer_->SetBounds(gfx::Size(10, 10));
            copy_layer_->AddChild(child_layer_);

            layer_tree()->SetRootLayer(root_);
            LayerTreeHostCopyRequestTest::SetupTree();
            client_.set_bounds(root_->bounds());
        }

        void BeginTest() override
        {
            PostSetNeedsCommitToMainThread();

            std::unique_ptr<CopyOutputRequest> request = CopyOutputRequest::CreateBitmapRequest(base::Bind(
                &LayerTreeHostCopyRequestTestScaledLayer::CopyOutputCallback,
                base::Unretained(this)));
            request->set_area(gfx::Rect(5, 5));
            copy_layer_->RequestCopyOfOutput(std::move(request));
        }

        void CopyOutputCallback(std::unique_ptr<CopyOutputResult> result)
        {
            // The request area is expressed in layer space, but the result's size takes
            // into account the transform from layer space to surface space.
            EXPECT_EQ(gfx::Size(10, 10), result->size());
            EndTest();
        }

        void AfterTest() override { }

        FakeContentLayerClient client_;
        scoped_refptr<Layer> root_;
        scoped_refptr<Layer> copy_layer_;
        scoped_refptr<FakePictureLayer> child_layer_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCopyRequestTestScaledLayer);

    class LayerTreeHostTestAsyncTwoReadbacksWithoutDraw
        : public LayerTreeHostCopyRequestTest {
    protected:
        void SetupTree() override
        {
            root_ = FakePictureLayer::Create(&client_);
            root_->SetBounds(gfx::Size(20, 20));

            copy_layer_ = FakePictureLayer::Create(&client_);
            copy_layer_->SetBounds(gfx::Size(10, 10));
            root_->AddChild(copy_layer_);

            layer_tree()->SetRootLayer(root_);
            LayerTreeHostCopyRequestTest::SetupTree();
            client_.set_bounds(root_->bounds());
        }

        void AddCopyRequest(Layer* layer)
        {
            layer->RequestCopyOfOutput(
                CopyOutputRequest::CreateBitmapRequest(base::Bind(
                    &LayerTreeHostTestAsyncTwoReadbacksWithoutDraw::CopyOutputCallback,
                    base::Unretained(this))));
        }

        void BeginTest() override
        {
            saw_copy_request_ = false;
            callback_count_ = 0;
            PostSetNeedsCommitToMainThread();

            // Prevent drawing.
            layer_tree()->SetViewportSize(gfx::Size(0, 0));

            AddCopyRequest(copy_layer_.get());
        }

        void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override
        {
            if (impl->active_tree()->source_frame_number() == 0) {
                EXPECT_TRUE(impl->active_tree()->LayerById(copy_layer_->id()));
                saw_copy_request_ = true;
            }
        }

        void DidCommit() override
        {
            if (layer_tree_host()->SourceFrameNumber() == 1) {
                // Allow drawing.
                layer_tree()->SetViewportSize(gfx::Size(root_->bounds()));

                AddCopyRequest(copy_layer_.get());
            }
        }

        void CopyOutputCallback(std::unique_ptr<CopyOutputResult> result)
        {
            EXPECT_TRUE(layer_tree_host()->GetTaskRunnerProvider()->IsMainThread());

            // The first frame can't be drawn.
            switch (callback_count_) {
            case 0:
                EXPECT_TRUE(result->IsEmpty());
                EXPECT_EQ(gfx::Size(), result->size());
                break;
            case 1:
                EXPECT_FALSE(result->IsEmpty());
                EXPECT_EQ(copy_layer_->bounds().ToString(), result->size().ToString());
                EndTest();
                break;
            default:
                NOTREACHED();
                break;
            }

            ++callback_count_;
        }

        void AfterTest() override { EXPECT_TRUE(saw_copy_request_); }

        bool saw_copy_request_;
        int callback_count_;
        FakeContentLayerClient client_;
        scoped_refptr<FakePictureLayer> root_;
        scoped_refptr<FakePictureLayer> copy_layer_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestAsyncTwoReadbacksWithoutDraw);

    class LayerTreeHostCopyRequestTestDeleteTexture
        : public LayerTreeHostCopyRequestTest {
    protected:
        std::unique_ptr<OutputSurface> CreateDisplayOutputSurfaceOnThread(
            scoped_refptr<ContextProvider> compositor_context_provider) override
        {
            display_context_provider_ = TestContextProvider::Create();
            display_context_provider_->BindToCurrentThread();
            return FakeOutputSurface::Create3d(display_context_provider_);
        }

        void SetupTree() override
        {
            root_ = FakePictureLayer::Create(&client_);
            root_->SetBounds(gfx::Size(20, 20));

            copy_layer_ = FakePictureLayer::Create(&client_);
            copy_layer_->SetBounds(gfx::Size(10, 10));
            root_->AddChild(copy_layer_);

            layer_tree()->SetRootLayer(root_);
            LayerTreeHostCopyRequestTest::SetupTree();
            client_.set_bounds(root_->bounds());
        }

        void BeginTest() override { PostSetNeedsCommitToMainThread(); }

        void ReceiveCopyRequestOutputAndCommit(
            std::unique_ptr<CopyOutputResult> result)
        {
            EXPECT_TRUE(layer_tree_host()->GetTaskRunnerProvider()->IsMainThread());
            EXPECT_EQ(gfx::Size(10, 10).ToString(), result->size().ToString());
            EXPECT_TRUE(result->HasTexture());

            // Save the result for later.
            EXPECT_FALSE(result_);
            result_ = std::move(result);

            // Post a commit to lose the output surface.
            layer_tree_host()->SetNeedsCommit();
        }

        void InsertCopyRequest()
        {
            copy_layer_->RequestCopyOfOutput(CopyOutputRequest::CreateRequest(
                base::Bind(&LayerTreeHostCopyRequestTestDeleteTexture::
                               ReceiveCopyRequestOutputAndCommit,
                    base::Unretained(this))));
        }

        void DestroyCopyResultAndCheckNumTextures()
        {
            EXPECT_TRUE(result_);
            result_ = nullptr;

            ImplThreadTaskRunner()->PostTask(
                FROM_HERE, base::Bind(&LayerTreeHostCopyRequestTestDeleteTexture::CheckNumTexturesAfterReadbackDestroyed, base::Unretained(this)));
        }

        void CheckNumTexturesAfterReadbackDestroyed()
        {
            // After the copy we had |num_textures_after_readback_| many textures, but
            // releasing the copy output request should cause the texture in the request
            // to be destroyed by the compositor, so we should have 1 less by now.
            EXPECT_EQ(num_textures_after_readback_ - 1,
                display_context_provider_->TestContext3d()->NumTextures());

            // Drop the reference to the context provider on the compositor thread.
            display_context_provider_ = nullptr;
            EndTest();
        }

        void DisplayDidDrawAndSwapOnThread() override
        {
            switch (num_swaps_++) {
            case 0:
                // The layers have been drawn, so any textures required for drawing have
                // been allocated.
                EXPECT_FALSE(result_);
                num_textures_without_readback_ = display_context_provider_->TestContext3d()->NumTextures();

                // Request a copy of the layer. This will use another texture.
                MainThreadTaskRunner()->PostTask(
                    FROM_HERE,
                    base::Bind(
                        &LayerTreeHostCopyRequestTestDeleteTexture::InsertCopyRequest,
                        base::Unretained(this)));
                break;
            case 1:
                // We did a readback, so there will be a readback texture around now.
                num_textures_after_readback_ = display_context_provider_->TestContext3d()->NumTextures();
                EXPECT_LT(num_textures_without_readback_, num_textures_after_readback_);

                // Now destroy the CopyOutputResult, releasing the texture inside back
                // to the compositor. Then check the resulting number of allocated
                // textures.
                MainThreadTaskRunner()->PostTask(
                    FROM_HERE, base::Bind(&LayerTreeHostCopyRequestTestDeleteTexture::DestroyCopyResultAndCheckNumTextures, base::Unretained(this)));
                break;
            }
        }

        void AfterTest() override { }

        scoped_refptr<TestContextProvider> display_context_provider_;
        int num_swaps_ = 0;
        size_t num_textures_without_readback_ = 0;
        size_t num_textures_after_readback_ = 0;
        FakeContentLayerClient client_;
        scoped_refptr<FakePictureLayer> root_;
        scoped_refptr<FakePictureLayer> copy_layer_;
        std::unique_ptr<CopyOutputResult> result_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCopyRequestTestDeleteTexture);

    class LayerTreeHostCopyRequestTestCountTextures
        : public LayerTreeHostCopyRequestTest {
    protected:
        void InitializeSettings(LayerTreeSettings* settings) override
        {
            // Always allocate only a single texture at a time through ResourceProvider.
            settings->renderer_settings.texture_id_allocation_chunk_size = 1;
        }

        std::unique_ptr<OutputSurface> CreateDisplayOutputSurfaceOnThread(
            scoped_refptr<ContextProvider> compositor_context_provider) override
        {
            // These tests expect the LayerTreeHostImpl to share a context with
            // the Display so that sync points are not needed and the texture counts
            // are visible together.
            // Since this test does not override CreateCompositorFrameSink, the
            // |compositor_context_provider| will be a TestContextProvider.
            display_context_provider_ = static_cast<TestContextProvider*>(compositor_context_provider.get());
            return FakeOutputSurface::Create3d(std::move(compositor_context_provider));
        }

        void SetupTree() override
        {
            // The layers in this test have solid color content, so they don't
            // actually allocate any textures, making counting easier.

            root_ = FakePictureLayer::Create(&root_client_);
            root_->SetBounds(gfx::Size(20, 20));
            root_client_.set_bounds(root_->bounds());

            copy_layer_ = FakePictureLayer::Create(&copy_client_);
            copy_layer_->SetBounds(gfx::Size(10, 10));
            copy_client_.set_bounds(copy_layer_->bounds());
            // Doing a copy makes the layer have a render surface which can cause
            // texture allocations. So get those allocations out of the way in the
            // first frame by forcing it to have a render surface.
            copy_layer_->SetForceRenderSurfaceForTesting(true);
            root_->AddChild(copy_layer_);

            layer_tree()->SetRootLayer(root_);
            LayerTreeHostCopyRequestTest::SetupTree();
        }

        void BeginTest() override
        {
            waited_sync_token_after_readback_.Clear();
            PostSetNeedsCommitToMainThread();
        }

        virtual void RequestCopy(Layer* layer) = 0;

        void DidCommit() override
        {
            switch (layer_tree_host()->SourceFrameNumber()) {
            case 1:
                // The layers have been pushed to the impl side and drawn. Any textures
                // that are created in that process will have been allocated.
                RequestCopy(copy_layer_.get());
                break;
            }
        }

        void DisplayDidDrawAndSwapOnThread() override
        {
            switch (num_swaps_++) {
            case 0:
                // The first frame has been drawn, so textures for drawing have been
                // allocated.
                num_textures_without_readback_ = display_context_provider_->TestContext3d()->NumTextures();
                break;
            case 1:
                // We did a readback, so there will be a readback texture around now.
                num_textures_with_readback_ = display_context_provider_->TestContext3d()->NumTextures();
                waited_sync_token_after_readback_ = display_context_provider_->TestContext3d()
                                                        ->last_waited_sync_token();

                // End the test after main thread has a chance to hear about the
                // readback.
                MainThreadTaskRunner()->PostTask(
                    FROM_HERE,
                    base::Bind(&LayerTreeHostCopyRequestTestCountTextures::DoEndTest,
                        base::Unretained(this)));
                break;
            }
        }

        virtual void DoEndTest() { EndTest(); }

        scoped_refptr<TestContextProvider> display_context_provider_;
        int num_swaps_ = 0;
        size_t num_textures_without_readback_ = 0;
        size_t num_textures_with_readback_ = 0;
        gpu::SyncToken waited_sync_token_after_readback_;
        FakeContentLayerClient root_client_;
        FakeContentLayerClient copy_client_;
        scoped_refptr<FakePictureLayer> root_;
        scoped_refptr<FakePictureLayer> copy_layer_;
    };

    class LayerTreeHostCopyRequestTestCreatesTexture
        : public LayerTreeHostCopyRequestTestCountTextures {
    protected:
        void RequestCopy(Layer* layer) override
        {
            // Request a normal texture copy. This should create a new texture.
            copy_layer_->RequestCopyOfOutput(
                CopyOutputRequest::CreateRequest(base::Bind(
                    &LayerTreeHostCopyRequestTestCreatesTexture::CopyOutputCallback,
                    base::Unretained(this))));
        }

        void CopyOutputCallback(std::unique_ptr<CopyOutputResult> result)
        {
            EXPECT_FALSE(result->IsEmpty());
            EXPECT_TRUE(result->HasTexture());

            TextureMailbox mailbox;
            result->TakeTexture(&mailbox, &release_);
            EXPECT_TRUE(release_);
        }

        void AfterTest() override
        {
            release_->Run(gpu::SyncToken(), false);

            // No sync point was needed.
            EXPECT_FALSE(waited_sync_token_after_readback_.HasData());
            // Except the copy to have made another texture.
            EXPECT_EQ(num_textures_without_readback_ + 1, num_textures_with_readback_);
        }

        std::unique_ptr<SingleReleaseCallback> release_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCopyRequestTestCreatesTexture);

    class LayerTreeHostCopyRequestTestProvideTexture
        : public LayerTreeHostCopyRequestTestCountTextures {
    protected:
        void BeginTest() override
        {
            external_context_provider_ = TestContextProvider::Create();
            EXPECT_TRUE(external_context_provider_->BindToCurrentThread());
            LayerTreeHostCopyRequestTestCountTextures::BeginTest();
        }

        void CopyOutputCallback(std::unique_ptr<CopyOutputResult> result)
        {
            EXPECT_FALSE(result->IsEmpty());
            EXPECT_TRUE(result->HasTexture());

            TextureMailbox mailbox;
            std::unique_ptr<SingleReleaseCallback> release;
            result->TakeTexture(&mailbox, &release);
            EXPECT_FALSE(release);
        }

        void RequestCopy(Layer* layer) override
        {
            // Request a copy to a provided texture. This should not create a new
            // texture.
            std::unique_ptr<CopyOutputRequest> request = CopyOutputRequest::CreateRequest(base::Bind(
                &LayerTreeHostCopyRequestTestProvideTexture::CopyOutputCallback,
                base::Unretained(this)));

            gpu::gles2::GLES2Interface* gl = external_context_provider_->ContextGL();
            gpu::Mailbox mailbox;
            gl->GenMailboxCHROMIUM(mailbox.name);

            const GLuint64 fence_sync = gl->InsertFenceSyncCHROMIUM();
            gl->ShallowFlushCHROMIUM();
            gl->GenSyncTokenCHROMIUM(fence_sync, sync_token_.GetData());

            request->SetTextureMailbox(
                TextureMailbox(mailbox, sync_token_, GL_TEXTURE_2D));
            EXPECT_TRUE(request->has_texture_mailbox());

            copy_layer_->RequestCopyOfOutput(std::move(request));
        }

        void AfterTest() override
        {
            // Expect the compositor to have waited for the sync point in the provided
            // TextureMailbox.
            EXPECT_EQ(sync_token_, waited_sync_token_after_readback_);
            // Except the copy to have *not* made another texture.
            EXPECT_EQ(num_textures_without_readback_, num_textures_with_readback_);
        }

        scoped_refptr<TestContextProvider> external_context_provider_;
        gpu::SyncToken sync_token_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCopyRequestTestProvideTexture);

    class LayerTreeHostCopyRequestTestDestroyBeforeCopy
        : public LayerTreeHostCopyRequestTest {
    protected:
        void SetupTree() override
        {
            root_ = FakePictureLayer::Create(&client_);
            root_->SetBounds(gfx::Size(20, 20));

            copy_layer_ = FakePictureLayer::Create(&client_);
            copy_layer_->SetBounds(gfx::Size(10, 10));
            root_->AddChild(copy_layer_);

            layer_tree()->SetRootLayer(root_);
            LayerTreeHostCopyRequestTest::SetupTree();
            client_.set_bounds(root_->bounds());
        }

        void BeginTest() override
        {
            callback_count_ = 0;
            PostSetNeedsCommitToMainThread();
        }

        void CopyOutputCallback(std::unique_ptr<CopyOutputResult> result)
        {
            EXPECT_TRUE(result->IsEmpty());
            ++callback_count_;
        }

        void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override
        {
            MainThreadTaskRunner()->PostTask(
                FROM_HERE,
                base::Bind(&LayerTreeHostCopyRequestTestDestroyBeforeCopy::DidActivate,
                    base::Unretained(this)));
        }

        void DidActivate()
        {
            switch (layer_tree_host()->SourceFrameNumber()) {
            case 1: {
                EXPECT_EQ(0, callback_count_);
                // Put a copy request on the layer, but then don't allow any
                // drawing to take place.
                std::unique_ptr<CopyOutputRequest> request = CopyOutputRequest::CreateRequest(
                    base::Bind(&LayerTreeHostCopyRequestTestDestroyBeforeCopy::
                                   CopyOutputCallback,
                        base::Unretained(this)));
                copy_layer_->RequestCopyOfOutput(std::move(request));

                layer_tree()->SetViewportSize(gfx::Size());
                break;
            }
            case 2:
                EXPECT_EQ(0, callback_count_);
                // Remove the copy layer before we were able to draw.
                copy_layer_->RemoveFromParent();
                break;
            case 3:
                EXPECT_EQ(1, callback_count_);
                // Allow us to draw now.
                layer_tree()->SetViewportSize(layer_tree()->root_layer()->bounds());
                break;
            case 4:
                EXPECT_EQ(1, callback_count_);
                // We should not have crashed.
                EndTest();
            }
        }

        void AfterTest() override { }

        int callback_count_;
        FakeContentLayerClient client_;
        scoped_refptr<FakePictureLayer> root_;
        scoped_refptr<FakePictureLayer> copy_layer_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCopyRequestTestDestroyBeforeCopy);

    class LayerTreeHostCopyRequestTestShutdownBeforeCopy
        : public LayerTreeHostCopyRequestTest {
    protected:
        void SetupTree() override
        {
            root_ = FakePictureLayer::Create(&client_);
            root_->SetBounds(gfx::Size(20, 20));

            copy_layer_ = FakePictureLayer::Create(&client_);
            copy_layer_->SetBounds(gfx::Size(10, 10));
            root_->AddChild(copy_layer_);

            layer_tree()->SetRootLayer(root_);
            LayerTreeHostCopyRequestTest::SetupTree();
            client_.set_bounds(root_->bounds());
        }

        void BeginTest() override
        {
            callback_count_ = 0;
            PostSetNeedsCommitToMainThread();
        }

        void CopyOutputCallback(std::unique_ptr<CopyOutputResult> result)
        {
            EXPECT_TRUE(result->IsEmpty());
            ++callback_count_;
        }

        void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override
        {
            MainThreadTaskRunner()->PostTask(
                FROM_HERE,
                base::Bind(&LayerTreeHostCopyRequestTestShutdownBeforeCopy::DidActivate,
                    base::Unretained(this)));
        }

        void DidActivate()
        {
            switch (layer_tree_host()->SourceFrameNumber()) {
            case 1: {
                EXPECT_EQ(0, callback_count_);
                // Put a copy request on the layer, but then don't allow any
                // drawing to take place.
                std::unique_ptr<CopyOutputRequest> request = CopyOutputRequest::CreateRequest(
                    base::Bind(&LayerTreeHostCopyRequestTestShutdownBeforeCopy::
                                   CopyOutputCallback,
                        base::Unretained(this)));
                copy_layer_->RequestCopyOfOutput(std::move(request));

                layer_tree()->SetViewportSize(gfx::Size());
                break;
            }
            case 2:
                DestroyLayerTreeHost();
                // End the test after the copy result has had a chance to get back to
                // the main thread.
                MainThreadTaskRunner()->PostTask(
                    FROM_HERE,
                    base::Bind(&LayerTreeHostCopyRequestTestShutdownBeforeCopy::EndTest,
                        base::Unretained(this)));
                break;
            }
        }

        void AfterTest() override { EXPECT_EQ(1, callback_count_); }

        int callback_count_;
        FakeContentLayerClient client_;
        scoped_refptr<FakePictureLayer> root_;
        scoped_refptr<FakePictureLayer> copy_layer_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCopyRequestTestShutdownBeforeCopy);

    class LayerTreeHostCopyRequestTestMultipleDrawsHiddenCopyRequest
        : public LayerTreeHostCopyRequestTest {
    protected:
        void SetupTree() override
        {
            scoped_refptr<FakePictureLayer> root = FakePictureLayer::Create(&client_);
            root->SetBounds(gfx::Size(20, 20));

            child_ = FakePictureLayer::Create(&client_);
            child_->SetBounds(gfx::Size(10, 10));
            root->AddChild(child_);
            child_->SetHideLayerAndSubtree(true);

            layer_tree()->SetRootLayer(root);
            LayerTreeHostCopyRequestTest::SetupTree();
            client_.set_bounds(root->bounds());
        }

        void BeginTest() override
        {
            num_draws_ = 0;
            copy_happened_ = false;
            draw_happened_ = false;
            PostSetNeedsCommitToMainThread();
        }

        void DidCommit() override
        {
            // Send a copy request after the first commit.
            if (layer_tree_host()->SourceFrameNumber() == 1) {
                child_->RequestCopyOfOutput(
                    CopyOutputRequest::CreateBitmapRequest(base::Bind(
                        &LayerTreeHostCopyRequestTestMultipleDrawsHiddenCopyRequest::
                            CopyOutputCallback,
                        base::Unretained(this))));
            }
        }

        DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
            LayerTreeHostImpl::FrameData* frame_data,
            DrawResult draw_result) override
        {
            LayerImpl* root = host_impl->active_tree()->root_layer_for_testing();
            LayerImpl* child = host_impl->active_tree()->LayerById(child_->id());

            bool saw_root = false;
            bool saw_child = false;
            for (LayerIterator it = LayerIterator::Begin(frame_data->render_surface_layer_list);
                 it != LayerIterator::End(frame_data->render_surface_layer_list);
                 ++it) {
                if (it.represents_itself()) {
                    if (*it == root)
                        saw_root = true;
                    else if (*it == child)
                        saw_child = true;
                    else
                        NOTREACHED();
                }
            }

            ++num_draws_;
            // The first draw has no copy request. The 2nd draw has a copy request, the
            // 3rd should not again.
            switch (num_draws_) {
            case 1:
                // Only the root layer draws, the child is hidden.
                EXPECT_TRUE(saw_root);
                EXPECT_FALSE(saw_child);
                break;
            case 2:
                // Copy happening here, the child will draw.
                EXPECT_TRUE(saw_root);
                EXPECT_TRUE(saw_child);
                // Make another draw happen after doing the copy request.
                host_impl->SetViewportDamage(gfx::Rect(1, 1));
                host_impl->SetNeedsRedraw();
                break;
            case 3:
                // If LayerTreeHostImpl does the wrong thing, it will try to draw the
                // layer which had a copy request. But only the root should draw.
                EXPECT_TRUE(saw_root);
                EXPECT_FALSE(saw_child);

                // End the test! Don't race with copy request callbacks, so post the end
                // to the main thread.
                draw_happened_ = true;
                MainThreadTaskRunner()->PostTask(
                    FROM_HERE,
                    base::Bind(
                        &LayerTreeHostCopyRequestTestMultipleDrawsHiddenCopyRequest::
                            TryEndTest,
                        base::Unretained(this)));
                break;
            }
            return draw_result;
        }

        void CopyOutputCallback(std::unique_ptr<CopyOutputResult> result)
        {
            EXPECT_FALSE(TestEnded());
            copy_happened_ = true;
            TryEndTest();
        }

        void TryEndTest()
        {
            if (draw_happened_ && copy_happened_)
                EndTest();
        }

        void AfterTest() override { }

        scoped_refptr<FakePictureLayer> child_;
        FakeContentLayerClient client_;
        int num_draws_;
        bool copy_happened_;
        bool draw_happened_;
    };

    SINGLE_AND_MULTI_THREAD_TEST_F(
        LayerTreeHostCopyRequestTestMultipleDrawsHiddenCopyRequest);

} // namespace
} // namespace cc
